package com.ccp.dev.workflow.bpmutil;

import com.ccp.dev.core.util.*;
import com.ccp.dev.form.consts.FormConstants;
import com.ccp.dev.workflow.model.ActDefModel;
import com.ccp.dev.workflow.model.bpm.ProcessCmd;
import com.ccp.dev.workflow.model.bpm.TaskExecutor;
import com.ccp.dev.workflow.service.ActDefModelService;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.flowable.bpmn.model.*;

import javax.servlet.http.HttpServletRequest;
import javax.xml.transform.TransformerFactoryConfigurationError;
import java.io.IOException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * ClassDescribe:
 *
 * @author :jiayueqiao
 * Date: 2019-06-19
 * Since:1
 */
public class BpmUtil {

    private static Pattern TITLE_PATTERN = Pattern.compile("\\{(.*?)\\}", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);

    /**
     * BPM的XML的命名空间
     */
    private final static String BPM_XML_NS = "xmlns=\"http://www.omg.org/spec/BPMN/20100524/MODEL\"";

    /**
     * 取得流程定义中的分支节点的设置条件 ，返回为该节点的跳转节点<ID，条件>的映射
     *
     * @param processXml
     *            流程定义
     * @param decisionNodeId
     *            分支节点ID
     * @return
     */
    public static Map<String, String> getDecisionConditions(String processXml, String decisionNodeId) {
        Map<String, String> map = new HashMap<String, String>();
        processXml = processXml.replace(BPM_XML_NS, "xmlns:bpm='casic'");
        Document doc = Dom4jUtil.loadXml(processXml);
        Element root = doc.getRootElement();

        // 添加分支流向
        List<Node> nodes = root.selectNodes("//sequenceFlow[@sourceRef='" + decisionNodeId + "']");
        for (Node node : nodes) {
            Element el = (Element) node;
            String id = el.attributeValue("targetRef");
            String condition = "";
            Element conditionNode = el.element("conditionExpression");
            if (conditionNode != null) {
                condition = conditionNode.getText().trim();
                condition = StringUtil.trimPrefix(condition, "${");
                condition = StringUtil.trimSufffix(condition, "}");
            }
            map.put(id, condition);
        }
        return map;
    }

    public static String setCondition(String sourceNode, Map<String, String> map, String xml) throws IOException {
        xml = xml.replace(BPM_XML_NS, "xmlns:bpm=\"casic\"");
        Document doc = Dom4jUtil.loadXml(xml, "utf-8");
        Element root = doc.getRootElement();
        List<Node> nodes = root.selectNodes("//sequenceFlow[@sourceRef='" + sourceNode + "']");
        for (Node node : nodes) {
            Element el = (Element) node;
            String id = el.attributeValue("targetRef");
            String condition = map.get(id);
            condition = StringUtil.trimPrefix(condition.trim(),"return");
            condition = StringUtil.trimSufffix(condition,";");
            condition = condition.trim();
            Element conditionEl = el.element("conditionExpression");
            if (conditionEl != null){
                el.remove(conditionEl);
            }
            if (StringUtil.isNotEmpty(condition)) {
                Element elAdd = el.addElement("conditionExpression");
                elAdd.addAttribute("xsi:type", "tFormalExpression");
                elAdd.addCDATA("${"+condition+"}");
            }
        }
        String outXml = doc.asXML();
        outXml = outXml.replace("xmlns:bpm=\"casic\"", BPM_XML_NS);
        return outXml;
    }

    /**
     * 从流程模型中获取脚本节点的脚本
     * @param nodeId 节点id
     * @param xml 流程模型xml
     * @return 脚本内容
     */
    public static String getScriptInModel(String nodeId, String xml) throws IOException {
        xml = xml.replace(BPM_XML_NS, "xmlns:bpm=\"casic\"");
        Document doc = Dom4jUtil.loadXml(xml, "utf-8");
        Element root = doc.getRootElement();
        Node node = root.selectSingleNode("//scriptTask[@id='" + nodeId + "']");
        Node scriptNode = node.selectSingleNode("//script");
        Element nodeElem = (Element) scriptNode;
        String script = nodeElem.getStringValue();
        return script;
    }

    /**
     * 在xml文件中添加脚本
     * @param nodeId 节点id
     * @param xml 模型xml
     * @return
     */
    public static String setScriptInModel(String nodeId, String xml, String script) throws IOException {
        xml = xml.replace(BPM_XML_NS, "xmlns:bpm=\"casic\"");
        Document doc = Dom4jUtil.loadXml(xml, "utf-8");
        Element root = doc.getRootElement();
        Node node = root.selectSingleNode("//scriptTask[@id='" + nodeId + "']");
        Node scriptNode = node.selectSingleNode("//script");
        Element nodeElem = (Element) scriptNode;
        nodeElem.clearContent();
        nodeElem.addCDATA(script);
        String outXml = doc.asXML();
        outXml = outXml.replace("xmlns:bpm=\"casic\"", BPM_XML_NS);
        return outXml;
    }

    /**
     * 根据标题规则获取任务标题。
     *
     * <pre>
     * 1.标题规则如下：
     * 		{流程标题:title}-{发起人:startUser}-{发起日期:startDate}
     * 		也可以简写为：
     * 		{title}-{startUser}-{startDate}
     * 2.map数据如下：
     * 		Map map=new HashMap();
     * 		map.put("title","发文流程");
     * 		map.put("startUser","ray");
     * 		map.put("startDate","2012-05-29");
     *
     * 3.解析结果如下:
     * 		发文流程-ray-2012-05-29
     * </pre>
     *
     * @param titleRule
     *            标题规则 ，标题规则写法 {流程标题:title}-{发起人:startUser}-{发起日期:startDate}
     * @param map
     *            任务变量map
     * @return
     */
    public static String getTitleByRule(String titleRule, Map<String, Object> map) {
        if (StringUtil.isEmpty(titleRule)){
            return "";
        }
        Matcher matcher = TITLE_PATTERN.matcher(titleRule);
        while (matcher.find()) {
            String tag = matcher.group(0);
            String rule = matcher.group(1);
            String[] aryRule = rule.split(":");
            String name = "";
            if (aryRule.length == 1) {
                name = rule;
            } else {
                name = aryRule[1];
            }
            if (map.containsKey(name)) {
                Object obj = map.get(name);
                if (BeanUtils.isNotEmpty(obj)) {
                    try {
                        titleRule = titleRule.replace(tag, obj.toString());
                    } catch (Exception e) {
                        titleRule = titleRule.replace(tag, "");
                    }
                } else {
                    titleRule = titleRule.replace(tag, "");
                }
            } else {
                titleRule = titleRule.replace(tag, "");
            }
        }
        return titleRule;
    }

    /**
     * 取得流程可以跳转的任务节点
     * @param bpmnModel 流程模型
     * @param nodeList 任务节点
     * @param flag 是否去除开始节点
     * @return Map对象，键名为任务id，键值为任务名称。
     */
    public static Map<String, Map<String, String>> getTranstoActivitys(BpmnModel bpmnModel, List<String> nodeList,boolean flag) {
        Map<String, Map<String, String>> actMap = getActivities(bpmnModel);
        if(flag){
            actMap.remove("开始节点");
        }
        Collection<Map<String, String>> values = actMap.values();
        for (String node : nodeList) {
            for (Map<String, String> map : values) {
                map.remove(node);
            }
        }

        return actMap;
    }

    /**
     * 从流程定义模型中获取流程任务id及名称
     * @param bpmnModel 流程模型
     * @return Map对象，键名为任务id，键值为任务名称。
     */
    public static Map<String, Map<String, String>> getActivities(BpmnModel bpmnModel) {
        Map<String, Map<String, String>> returnMap = new HashMap<>();

        Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements();
        Map<String, String> taskMap = new HashMap<>();
        Map<String, String> gateWayMap = new HashMap<>();
        Map<String, String> startMap = new HashMap<>();
        Map<String, String> endMap = new HashMap<>();
        Map<String, String> serviceMap = new HashMap<>();
        for (FlowElement element : flowElements) {
            if (element instanceof UserTask) {
                taskMap.put(element.getId(), element.getName());
            }
            if (element instanceof CallActivity) {
                taskMap.put(element.getId(), element.getName());
            }
            if (element instanceof Gateway) {
                gateWayMap.put(element.getId(), element.getName());
            }
            if (element instanceof StartEvent) {
                if (StringUtil.isEmpty(element.getName())) {
                    element.setName("开始节点");
                }
                startMap.put(element.getId(), element.getName());
            }
            if (element instanceof EndEvent) {
                if (StringUtil.isEmpty(element.getName())) {
                    element.setName("结束节点");
                }
                endMap.put(element.getId(), element.getName());
            }
            if (element instanceof ServiceTask) {
                serviceMap.put(element.getId(), element.getName());
            }
        }
        returnMap.put("网关节点", gateWayMap);
        returnMap.put("任务节点", taskMap);
        returnMap.put("开始节点", startMap);
        returnMap.put("结束节点", endMap);
        returnMap.put("自动任务", serviceMap);

        return returnMap;
    }

    /**
     * 根据任务执行人字符串返回执行人列表。
     *
     * @param executors 执行人为 user^id^名称，group^id^名称
     * @return 执行人列表。
     */
    public static List<TaskExecutor> getTaskExecutors(String executors) {
        String[] aryExecutor = executors.split("#");
        List<TaskExecutor> list = new ArrayList<TaskExecutor>();
        for (String tmp : aryExecutor) {
            String[] aryTmp = tmp.split("\\^");
            if (aryTmp.length == 3) {
                list.add(new TaskExecutor(aryTmp[0], aryTmp[1], aryTmp[2]));
            } else if (aryTmp.length == 1) {
                list.add(new TaskExecutor(aryTmp[0]));
            }
        }
        return list;
    }

    /**
     * 流程命令
     * @param request
     * @return
     */
    public static ProcessCmd getProcessCmd(HttpServletRequest request){
        ProcessCmd cmd = new ProcessCmd();
        String temp = request.getParameter("taskId");
        if (StringUtil.isNotEmpty(temp)) {
            cmd.setTaskId(temp);
        }


        temp = request.getParameter("formData");
        if (StringUtil.isNotEmpty(temp)) {
            temp = StringEscapeUtils.unescapeHtml4(temp);
            cmd.setFormData(temp);
        }

        Map paraMap = RequestUtil.getParameterValueMap(request, false, false);
        //添加nextPathId信息
        String[] array = RequestUtil.getParameterValues(request,"nextPathId");
        if(null!=array && array.length>0){
            paraMap.put("nextPathId",StringUtils.join(array,","));
        }
        //表单所有参数
        cmd.setFormDataMap(paraMap);
        ActDefModelService actDefModelService = AppUtil.getBean(ActDefModelService.class);
        ActDefModel actDefModel = null;
        temp = request.getParameter("procDefId");
        if (StringUtil.isNotEmpty(temp)) {
            cmd.setProcDefId(temp);
            actDefModel = actDefModelService.getByProcDefId(temp);
        } else {
            temp = request.getParameter("modelKey");
            if (StringUtil.isNotEmpty(temp)) {
                cmd.setModelKey(temp);
                actDefModel = actDefModelService.getMainByDefKey(temp);
            }
        }

        //临时变量
        cmd.addTransientVar("actDefModel", actDefModel);
        if (BeanUtils.isNotEmpty(actDefModel)) {
            String informType = "";
            informType = actDefModel.getInformType();
            cmd.setInformType(informType);
        }

        //目标任务
        temp = request.getParameter("destTask");
        if (StringUtil.isNotEmpty(temp)) {
            cmd.setDestTask(temp);
        }

        //业务主键
        temp = request.getParameter("businessKey");
        if (StringUtil.isNotEmpty(temp)) {
            cmd.setBusinessKey(temp);
        }

        //下一步任务节点
        String[] destTaskIds = RequestUtil.getParameterValues(request,"lastDestTaskId");
        String[] vnames;
        String val;
        if (destTaskIds != null) {
            cmd.setLastDestTaskIds(destTaskIds);
            String[] destTaskUserIds = new String[destTaskIds.length];

            for(int i = 0; i < destTaskIds.length; ++i) {
                vnames = RequestUtil.getParameterValues(request,destTaskIds[i] + "_userId");
                StringBuffer sb = new StringBuffer();
                if (vnames != null && vnames.length > 0) {
                    for(int j = 0; j < vnames.length; ++j) {
                        sb.append(vnames[j] + "#");
                    }
                }
                val = sb.toString();
                destTaskUserIds[i] = val;
            }
            //下一步任务节点的执行人
            cmd.setLastDestTaskUids(destTaskUserIds);
        }

        //驳回到任意节点时选择的节点
        temp = request.getParameter("backNodeIds");
        cmd.setBackNodeIds(temp);

      temp = request.getParameter("back");
        if (StringUtil.isNotEmpty(temp)) {
            Integer rtn = Integer.parseInt(temp);
            cmd.setBack(rtn);
        }

        cmd.setVoteContent(request.getParameter("voteContent"));
        String attachment = request.getParameter("attachment");
        attachment = StringEscapeUtils.unescapeHtml4(attachment);
        cmd.setAttachment(attachment);
        temp = request.getParameter("stackId");
        if (StringUtils.isNotEmpty(temp)) {
            cmd.setStackId(temp);
        }

        temp = request.getParameter("voteAgree");
        if (StringUtil.isNotEmpty(temp)) {
            cmd.setVoteAgree(new Short(temp));
        }

        Enumeration paramEnums = request.getParameterNames();


        while(paramEnums.hasMoreElements()) {
            String paramName = (String)paramEnums.nextElement();
            if (paramName.startsWith("v_")) {
                vnames = paramName.split("[_]");
                if (vnames != null && vnames.length == 3) {
                    String varName = vnames[1];
                    val = request.getParameter(paramName);
                    if (!val.isEmpty()) {
                        Object valObj = getValue(vnames[2], val);
                        cmd.getVariables().put(varName, valObj);
                    }
                }
            }
            if(paramName.endsWith(FormConstants.DATA_PRIVACY_LEVEL)){
                //将在线表单的密级设置到流程变量中
                val = request.getParameter(paramName);
                cmd.addVariable(FormConstants.DATA_PRIVACY_LEVEL,val);
            }
        }

        temp = request.getParameter("isManage");
        if (StringUtil.isNotEmpty(temp)) {
            cmd.setIsManage(new Short(temp));
        }

        temp = request.getParameter("_executors_");
        if (StringUtil.isNotEmpty(temp)) {
            List<TaskExecutor> executorList = getTaskExecutors(temp);
            cmd.setTaskExecutors(executorList);
        }


        temp = request.getParameter("startNode");
        if (StringUtil.isNotEmpty(temp)) {
            cmd.setStartNode(temp);
        }

        String relRunId = RequestUtil.getString(request, "relRunId", "");
        cmd.setRelRunId(relRunId);
        return cmd;
    }

    /**
     * 根据不同类型获得值
     * @param type
     * @param paramValue
     * @return
     */
    public static Object getValue(String type, String paramValue) {
        Object value = null;
        if ("S".equals(type)) {
            value = paramValue;
        } else if ("L".equals(type)) {
            value = new Long(paramValue);
        } else if ("I".equals(type)) {
            value = new Integer(paramValue);
        } else if ("DB".equals(type)) {
            value = new java.lang.Double(paramValue);
        } else if ("BD".equals(type)) {
            value = new BigDecimal(paramValue);
        } else if ("F".equals(type)) {
            value = new Float(paramValue);
        } else if ("SH".equals(type)) {
            value = new Short(paramValue);
        } else if ("D".equals(type)) {
            try {
                value = DateUtils.parseDate(paramValue, new String[]{"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss"});
            } catch (Exception var4) {
            }
        } else {
            value = paramValue;
        }

        return value;
    }

    /** 验证handler输入是否是否有效。
     *
     * <pre>
     *     handler 输入规则。
     *  spring的 serviceId +“." + 方法名称。
     * </pre>
     *
     * @param handler
     *            spring 的serviceId + "." + 方法名
     * @return 0 有效，-1，格式不对，-2 没有找到service类，-3没有找到对应的方法，-4，未知的错误。
     */
    public static int isHandlerValid(String handler) {
        if (!handler.contains(StringPool.DOT)) {
            return -1;
        }

        String[] aryHandler = handler.split("[.]");
        if (aryHandler.length != 2) {
            return -1;
        }

        String beanId = aryHandler[0];
        String method= aryHandler[1];
        Object serviceBean = null;

        try {
            serviceBean = AppUtil.getBean(beanId);
        } catch (Exception e) {
            return -2;
        }

        if (serviceBean == null) {
            return -2;
        }

        try {
            Method invokeMethod = serviceBean.getClass().getMethod(method, new Class[]{ProcessCmd.class});
            return 0;
        } catch (NoSuchMethodException e) {
            return -3;
        } catch (Exception e) {
            return -4;
        }
    }
}
